#include <Python.h>
#include <windows.h>
#include <winsock2.h>
#include <stdarg.h>
#include <mysql.h>
#define MAX_CONNECTION 10

static const char* UNKNOW_ERROR="Unknow error exception";
static const char* ARGUMENT_ERROR="Arguments are parse error";
static const char* CLOSED_RESULT_SET="Result was closed!";
static const void* connection[MAX_CONNECTION]; // MYSQL *
static const void* resultset[MAX_CONNECTION]; // MYSQL*,MYSQL_RES*
static int connection_count=0;

static void string_time(const char buf[]);
static void debug_log(const char* fmt,...);
// debug
static FILE* fp;
static void empyt(void* buf[],int size){
	int i=0;
	for(;i<size;i++) buf[i]=NULL;
}
static void add_resultset(MYSQL *mysql,MYSQL_RES *res){
	int i;
	for(i=0;i<connection_count;i++){
		if(mysql==connection[i]){
			resultset[i]=res;
			return;
		}
	}
}
static MYSQL_RES * last_resultset(MYSQL *mysql){
	int i;
	for(i=0;i<connection_count;i++){
		if(mysql==connection[i]){
			return resultset[i];
		}
	}
	return NULL;
}
static void del_resultset(MYSQL *mysql){
	int i;
	for(i=0;i<connection_count;i++){
		if(mysql==connection[i]){
			mysql_free_result(resultset[i]);
			resultset[i]=NULL;
		}
	}
}
static void clean_up(){
	int i;
	debug_log("Trace end\n");
	fclose(fp);
	for(i=0;i<connection_count;i++){
		mysql_close((MYSQL*)connection[i]);
	}
	connection_count=0;
}

/*
add a  MYSQL instance
*/
static void add(MYSQL *mysql){
	connection[connection_count]=(void*)mysql;
	connection_count++;
	debug_log("Conncetion %d, mysql object: %x\n",connection_count,mysql);
}
/*
delete a MYSQL instance
*/
static void del(MYSQL *mysql){
	if(connection_count==0)return;
	int i= 0;
	int shift=0;
	for(;i<connection_count;i++){
		if(mysql==connection[i]){
			mysql_close(mysql);
			//debug
			debug_log("disconnect %x\n",(int)connection[i]);
			connection[i]=NULL;
			free((void*)mysql);
			shift=1;
		}
		if(shift){
			if(i<MAX_CONNECTION-1){
				connection[i]=connection[i+1];
			}
		}
	}
	connection_count--;
}


static PyObject *myapiError;
static PyObject * description(MYSQL *mysql){
	MYSQL_RES *res=last_resultset(mysql);
	if(res==NULL) {
		debug_log("result is null\n");
		return NULL;
	}
	int count=mysql_num_fields(last_resultset(mysql));
	int i;
	PyObject *new_tuple = PyTuple_New(count);
	debug_log("number of field:%d\n",count);
	for(i=0;i<count;i++){
		MYSQL_FIELD *field=mysql_fetch_field(res);
		int not_null=0;
		if (field->flags & NOT_NULL_FLAG)
			not_null=1;
		PyObject *o=Py_BuildValue("(siiiiii)",field->name,field->type,field->length,field->decimals,field->max_length,0,not_null);
		PyTuple_SetItem(new_tuple,i,o);
	}
//	Py_DECREF(new_tuple);
	return new_tuple;
}
void parseParam(PyObject *tuple){
	if(PyTuple_Check(tuple)){
		int size=PyTuple_GET_SIZE(tuple);
		int i;
		for(i=0;i<size;++i){
			PyObject *obj;
			obj=PyTuple_GET_ITEM(tuple,i);
			if(PyLong_Check(obj)){
				int val=PyLong_AsLong(obj);
				debug_log("param %d is:%d\n",i,val);
			} else if(PyFloat_Check(obj)){
				double val=PyFloat_AsDouble(obj);
				debug_log("param %d is:%f\n",i,val);
			} else {
				const char* val=PyBytes_AsString(obj);
				debug_log("param %d is:%s\n",i,val);
			}
		}
		
	}else
		debug_log("is not tuple\n");
}
// if failure will return (1,error_message)
static PyObject * myapi_execute(PyObject *self,PyObject *args){
	void* p_mysql;
	const char * operation;
	PyObject *tuple;

	int ok=PyArg_ParseTuple(args,"isO",&p_mysql,&operation,&tuple);
	if(ok){
		PyObject *retval;
		parseParam(tuple);
		debug_log("SQL:%s\n",operation);
		if(mysql_real_query((MYSQL *)p_mysql,operation,strlen(operation))){
			const char* db_msg=mysql_error((MYSQL *)p_mysql);
			debug_log("Error message from db:%s\n",db_msg);
			retval=Py_BuildValue("is",1,db_msg);
		} else {
			MYSQL_RES *res;
			if(res=mysql_store_result((MYSQL *)p_mysql)){
				add_resultset(p_mysql,res);
				retval=Py_BuildValue("iO",0,description(p_mysql));
				if(retval==NULL){
					retval=Py_BuildValue("is",1,"get description error");
				}
			} else {
				retval=Py_BuildValue("is",1,mysql_error((MYSQL *)p_mysql));
			}
		}
//		Py_DECREF(retval);
		return retval;
	}else{
		PyErr_SetString(myapiError,ARGUMENT_ERROR);
		return NULL;
	}
}
static PyObject * myapi_resultset_close(PyObject *self,PyObject *args){
	void *p_mysql;
	MYSQL_RES *res;
	int ok=PyArg_ParseTuple(args,"i",&p_mysql);
	if(!ok){
		PyErr_SetString(myapiError,ARGUMENT_ERROR);
		return NULL;
	}
	del_resultset(p_mysql);
	return Py_None;
}
static PyObject * myapi_close(PyObject *self,PyObject *args){
	void* p_mysql;
	int ok=PyArg_ParseTuple(args,"i",&p_mysql);
	del(p_mysql);
	if(ok)
		return Py_None;
	else{
		PyErr_SetString(myapiError,UNKNOW_ERROR);
		return NULL;
	}
}
static PyObject * myapi_rowcount(PyObject *self,PyObject *args){
	void* p_mysql;
	PyArg_ParseTuple(args,"i",&p_mysql);
	MYSQL_RES *res=last_resultset(p_mysql);
	if(res==NULL) {
		PyErr_SetString(myapiError,CLOSED_RESULT_SET);
		return NULL;
	}
	my_ulonglong row_count=mysql_num_rows(res);
	return Py_BuildValue("i",row_count);
}
static PyObject * myapi_connect(PyObject *self,PyObject *args){
	const char *host;
	int port;
	const char *user,*passwd,*db,*charset;
	PyArg_ParseTuple(args,"sisssz",&host,&port,&user,&passwd,&db,&charset);
	if(connection_count==MAX_CONNECTION){
		Py_INCREF(myapiError);
		PyErr_SetString(myapiError,"Reach the max connection");
		return NULL;
	}
	PyObject *obj;
	MYSQL *mysql=malloc(sizeof(MYSQL));
	if(mysql_init(mysql)){
		add(mysql);
		debug_log("total connection:%d\n",connection_count);
	} else {
		Py_INCREF(myapiError);
		PyErr_SetString(myapiError,mysql_error(mysql));
		return NULL;
	}


	mysql_options(mysql, MYSQL_SET_CHARSET_NAME, charset) ;
	if(!mysql_real_connect(mysql,host,user,passwd,db,port,NULL,0)){
		Py_INCREF(myapiError);
		PyErr_SetString(myapiError,mysql_error(mysql));
		return NULL;
	} else
		obj=Py_BuildValue("i",mysql);
	return obj;
}
static PyMethodDef myapiMethods[]={
	{"connect",myapi_connect,METH_VARARGS,"create a connection.(host,port,user,passwd,db,charset)"},
	{"close",myapi_close,METH_VARARGS,"close connection"},
	{"execute", myapi_execute ,METH_VARARGS,"execute a sql script"},
	{"rowcount", myapi_rowcount ,METH_VARARGS,"row number of last resultset"},
	{"resultsetClose", myapi_resultset_close ,METH_VARARGS,"closing resultset"},
	{NULL,NULL,0,NULL}
};
static struct PyModuleDef pymymodule = {
	PyModuleDef_HEAD_INIT,
	"myapi",
	NULL,
	-1,
	myapiMethods
};
PyMODINIT_FUNC PyInit_myapi(void){
	empyt(connection,MAX_CONNECTION);
	empyt(resultset,MAX_CONNECTION);
	fp=fopen("pymy3000.log","a+");
	debug_log("Trace start\n");
	PyObject *m;
	m= PyModule_Create(&pymymodule);
	if(m==NULL) return NULL;
	myapiError = PyErr_NewException("pymy3000.myapi.Error",PyExc_BaseException,NULL);
	PyModule_AddObject(m,"error",myapiError);
	int ok=Py_AtExit(clean_up);
	if(ok){
		PyErr_SetString(myapiError,"clean_up function setting error");
		return NULL;
	}
	return m;
}
time_t current_time(){
	time_t t;
	time(&t);
	return t;
}
void string_time(const char buf[]){
	SYSTEMTIME systime;
	GetLocalTime(&systime);
	sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d",systime.wYear,systime.wMonth,systime.wDay,systime.wHour,systime.wMinute,systime.wSecond);
}
static const char string_date[100];
void debug_log(const char* format, ...){
	va_list ap;
	va_start(ap,format);
	string_time(string_date);
	fprintf(fp,"[%s]",string_date);
	vfprintf(fp,format,ap);
	va_end(ap);
}
